#pragma once
#include "../comm/util.hpp"
#include "../comm/log.hpp"


namespace ns_compile
{
    using namespace ns_util;
    using namespace ns_log;

    /*
    * 由于编译时会产生临时文件，因此我们创建一个tmp文件，专门保存临时文件
    * 所有临时文件都在该路径下
    */

    class Compiler
    {
        public:
        Compiler(){}
        ~Compiler(){}    
        /* 
        * 只负责编译
        * 形参，代码文件名，无需带路径与后缀
        * 内部自动添加路径和后缀
        * test -> test.cpp  //源文件
        * test -> test.exe  //可执行程序（文件）
        * test -> test.stderr  //编译错误信息文件
        */
        static bool Compile(const std::string& file_name)
        {
            pid_t pid = fork();

            if(pid < 0)
            {
                LOG(ERROR) << "编译时创建子进程失败" << std::endl;
                return false;
            }

            else if(pid == 0)
            {
                //子进程

                //重定向stderr到临时文件(保存gcc编译错误信息)
                umask(0);
                int errfd = open(PathUtil::CompileError(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                if(errfd < 0)
                {
                    LOG(WARNING) << "打开编译错误信息文件(strerr)失败" << std::endl;
                    exit(1);
                }

                dup2(errfd, 2);
                

                //进程替换
                execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(), 
                PathUtil::Src(file_name).c_str(), "-std=c++11", "-D", "COMPILE_ONLINE", nullptr);

                LOG(ERROR) << "编译时进程替换失败,请检查gcc参数是否正确" << std::endl;
                exit(2);
            }

            else
            {
                //父进程
                waitpid(pid, nullptr, 0);

                //判断是否编译成功（是否生成可执行程序）
                if(FileUtil::IsExist(PathUtil::Exe(file_name)))
                {
                    LOG(INFO) << "编译成功" << std::endl;
                    return true;
                }

                LOG(WARING) << "编译失败,请检查代码编写是否有误" << std::endl;
                return false;
            }
        }
    };
}
